package com.lock;

import com.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * @author tiny
 * @date 2022/1/8 17:45
 * @Description: 可重入的分布式锁
 *
 * 需要对客户端的 set 方法进行包装，使用线程的 Threadlocal 变量
 * 存储当前持有锁的计数。
 */
@Component
public class RedisWithReentrantLock {

    @Autowired
    private RedisUtil redisUtil;

    /**
     * 用map来维护lock_key-锁计数
     * 用ThreadLocal管理,保证线程安全
     */
    private ThreadLocal<Map<String,Integer>> locks = new ThreadLocal<Map<String,Integer>>();;

    /**
     * (底层)上锁
     */
    private boolean rLock(String key,long unixTime){
        Long time = redisUtil.set(key, "", unixTime);
        return time!=null && time.compareTo(0L)>=0;
    }

    /**
     * (底层)解锁
     * @return
     */
    private boolean rUnLock(String key){
        Long del = redisUtil.del(key);
        return del!=null && del.compareTo(0L)>0;
    }

    private Map<String,Integer> currentLocks(){
        Map<String, Integer> map = locks.get();
        if(map==null){
            map = new HashMap<String, Integer>();
        }
        return map;
    }

    /**
     * 上锁(提供给外界使用的)
     * @return
     */
    public synchronized boolean lock(String key,long unixTime){
        Map<String, Integer> map = currentLocks();
        if( map.get(key)!=null ){
            map.put(key,1);
        }else {
            map.put(key,map.get(key)+1);
        }
        return rLock(key,unixTime);
    }

    /**
     * 解锁(提供给外界使用的)
     * @return
     */
    public synchronized boolean unLock(String key){
        Map<String, Integer> map = currentLocks();
        Integer counts = map.get(key);
        if( counts==null || counts<=0 ){
            throw new RuntimeException();
        }else if(counts-1==0){
            map.remove(key);
        }else {
            map.put(key,counts-1);
        }
        return unLock(key);
    }
}
